package org.terramagnet.mvcframework.query;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.terramagnet.mvcframework.ApplicationRuntimeException;
import org.terramagnet.mvcframework.friendly.I18nFriendly;

/**
 * 接口请求封装.
 *
 * @author lip
 */
public final class Query {

    private static final Logger logger = LoggerFactory.getLogger(Query.class);

    private MultiValueMap<String, String> root;
    private ConversionService conversionService;

    public void setRoot(MultiValueMap<String, String> root) {
        this.root = root;
    }

    public Object expose() {
        return root == null ? null : root.toSingleValueMap();
    }

    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    /**
     * 加入一个参数到查询条件中.
     */
    public void addParameter(String name, String value) {
        if (this.root == null) {
            root = new LinkedMultiValueMap<String, String>();
        }
        root.add(name, value);
    }

    /**
     * 获取参数值. 若不存在则使用指定值初始化查询条件.
     *
     * @param <T> 参数值类型
     * @param name 参数名称
     * @param defaultValue 参数默认值
     * @return 参数值
     */
    public <T> T getOrCreateParameter(String name, T defaultValue) {
        if (this.root == null) {
            root = new LinkedMultiValueMap<String, String>();
        }
        T v = (T) getParameter(name, defaultValue.getClass());
        if (v == null) {
            addParameter(name, conversionService.convert(defaultValue, String.class));
        }
        return v;
    }

    /**
     * 获取简单请求参数. 找不到时返回{@code null}.
     *
     * @param name 参数名
     * @param type 参数值类型
     * @return 请求参数或{@code null}
     */
    public <T> T getParameter(String name, Class<T> type) {
        return getParameter(name, type, null);
    }

    /**
     * 获取简单请求参数. 找不到时返回空列表.
     *
     * @param name 参数名
     * @param type 参数值类型
     * @return 请求参数或{@code null}
     */
    public <T> List<T> getMultiParameter(String name, Class<T> type) {
        return getMultiParameter(name, type, null);
    }

    /**
     * 获取简单请求参数. 找不到时返回{@code defaultValue}.
     *
     * @param name 参数名
     * @param type 参数值类型
     * @param defaultValue 默认返回类型
     * @return 请求参数或{@code null}
     */
    public <T> T getParameter(String name, Class<T> type, T defaultValue) {
        if (root == null) {
            return null;
        }
        if (StringUtils.isBlank(name)) {
            return defaultValue;
        }
        String text = root.getFirst(name);
        if (type == null) {
            throw new NullPointerException("parameter type is required!");
        }

        if (type.isAssignableFrom(String.class)) {
            return (T) text;
        }
        if (!conversionService.canConvert(String.class, type)) {
            logger.warn("cannot covert \"{}\" to {}, return null instead！", text, type);
            return null;
        }
        try {
            return conversionService.convert(text, type);
        } catch (ConversionException ex) {
            logger.warn("cannot covert \"{}\" to {}, return null instead！", text, type);
            return null;
        }
    }

    /**
     * 获取简单请求参数. 找不到时返回{@code defaultValue}.
     *
     * @param name 参数名
     * @param type 参数值类型
     * @param defaultValue 默认返回类型
     * @return 请求参数或{@code null}
     */
    public <T> List<T> getMultiParameter(String name, Class<T> type, T defaultValue) {
        if (root == null) {
            return Collections.EMPTY_LIST;
        }
        if (type == null) {
            throw new NullPointerException("parameter type is required!");
        }
        List<String> multiValues = root.get(name);
        ArrayList<T> list = new ArrayList<T>(multiValues.size());
        for (String v : multiValues) {
            if (type.isAssignableFrom(String.class)) {
                list.add((T) v);
                if (!conversionService.canConvert(String.class, type)) {
                    logger.warn("Ignore a value of {} ,because the value \"{}\" cannot be covert to {}！", name, v, type);
                    continue;
                }
                try {
                    T convert = conversionService.convert(v, type);
                    list.add(convert);
                } catch (ConversionException ex) {
                    logger.warn("Ignore a value of {} ,because the value \"{}\" cannot be covert to {}！", name, v, type);
                }
            }
        }
        list.trimToSize();
        return list;
    }

    /**
     * 获取简单请求参数. 找不到时抛出{@link ApplicationRuntimeException}.
     *
     * @param name 参数名
     * @param type 参数值类型
     * @return 请求参数
     */
    public <T> T requireParameter(String name, Class<T> type) throws ApplicationRuntimeException {
        T value = getParameter(name, type, null);
        if (value == null) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, value);
        }
        return value;
    }

    /**
     * 获取int型参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public int requireInt(String name) throws ApplicationRuntimeException {
        return requireParameter(name, Integer.class);
    }

    /**
     * 获取正整型Int参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public int requirePositiveInt(String name) throws ApplicationRuntimeException {
        int i = requireParameter(name, Integer.class);
        if (i <= 0) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, i);
        }
        return i;
    }

    /**
     * 获取long型参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Long requireLong(String name) throws ApplicationRuntimeException {
        return requireParameter(name, Long.class);
    }

    /**
     * 获取正整型Long参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public long requirePositiveLong(String name) throws ApplicationRuntimeException {
        long i = requireParameter(name, Long.class);
        if (i <= 0) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, i);
        }
        return i;
    }

    /**
     * 获取float型参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Float requireFloat(String name) throws ApplicationRuntimeException {
        return requireParameter(name, Float.class);
    }

    /**
     * 获取正的Float参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public float requirePositiveFloat(String name) throws ApplicationRuntimeException {
        float i = requireParameter(name, Float.class);
        if (i <= 0) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, i);
        }
        return i;
    }

    /**
     * 获取double型参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Double requireDouble(String name) throws ApplicationRuntimeException {
        return requireParameter(name, Double.class);
    }

    /**
     * 获取正的Double参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public double requirePositiveDouble(String name) throws ApplicationRuntimeException {
        double i = requireParameter(name, Double.class);
        if (i <= 0) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, i);
        }
        return i;
    }

    /**
     * 获取number型参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Number requireNumber(String name) throws ApplicationRuntimeException {
        return requireParameter(name, Number.class);
    }

    /**
     * 获取正的Number参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Number requirePositiveNumber(String name) throws ApplicationRuntimeException {
        Number i = requireParameter(name, Number.class);
        if (i.doubleValue() <= 0) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, i);
        }
        return i;
    }

    /**
     * 获取String型参数.
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public String requireString(String name) throws ApplicationRuntimeException {
        return requireParameter(name, String.class);
    }

    /**
     * 获取Enum型参数.
     *
     * @param name 参数名
     * @param enumType 枚举类型
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public <T extends Enum<T>> T requireEnum(String name, Class<T> enumType) throws ApplicationRuntimeException {
        return requireParameter(name, enumType);
    }

    /**
     * 获取Date型参数. 格式化模板：{@code yyyy-MM-dd}
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Date requireDate(String name) throws ApplicationRuntimeException {
        String value = requireParameter(name, String.class);
        try {
            return DateUtils.parseDate(value, "yyyy-MM-dd");
        } catch (ParseException ex) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, value);
        }
    }

    /**
     * 获取Date型参数. 格式化模板：{@code yyyy-MM-dd HH:mm:ss}
     *
     * @param name 参数名
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Date requireTime(String name) throws ApplicationRuntimeException {
        String value = requireParameter(name, String.class);
        try {
            return DateUtils.parseDate(value, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException ex) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, value);
        }
    }

    /**
     * 使用指定pattern获取Date型参数.
     *
     * @param name 参数名
     * @param pattern 日期格式
     * @return 参数值
     * @throws ApplicationRuntimeException 参数不合法
     */
    public Date requireDateTime(String name, String pattern) throws ApplicationRuntimeException {
        String value = requireParameter(name, String.class);
        try {
            return DateUtils.parseDate(value, pattern);
        } catch (ParseException ex) {
            throw new ApplicationRuntimeException(I18nFriendly.ILLEGAL_ARGUMENT_VALUE, name, value);
        }
    }

    /**
     * 获取int型参数. 参数不合法时返回{@code 0}
     *
     * @param name 参数名
     * @return 参数值或{@code 0}
     */
    public int getIntParameter(String name) {
        return getParameter(name, Integer.class, 0);
    }

    /**
     * 获取int型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public int getIntParameter(String name, int defaultValue) {
        return getParameter(name, Integer.class, defaultValue);
    }

    /**
     * 获取正的int型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @return 参数值或{@code defaultValue}
     */
    public int getPositiveIntParameter(String name, int defaultValue) {
        int v = getIntParameter(name, defaultValue);
        return v > 0 ? v : defaultValue;
    }

    /**
     * 获取long型参数. 参数不合法时返回{@code 0}
     *
     * @param name 参数名
     * @return 参数值或{@code 0}
     */
    public long getLongParameter(String name) {
        return getLongParameter(name, 0);
    }

    /**
     * 获取long型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public long getLongParameter(String name, long defaultValue) {
        return getParameter(name, Long.class, defaultValue);
    }

    /**
     * 获取正的long型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public long getPositiveLongParameter(String name, long defaultValue) {
        long v = getLongParameter(name, defaultValue);
        return v > 0 ? v : defaultValue;
    }

    /**
     * 获取float型参数. 参数不合法时返回{@code 0}
     *
     * @param name 参数名
     * @return 参数值或{@code 0}
     */
    public float getFloatParameter(String name) {
        return getFloatParameter(name, 0);
    }

    /**
     * 获取float型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public float getFloatParameter(String name, float defaultValue) {
        return getParameter(name, Float.class, defaultValue);
    }

    /**
     * 获取正的float型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public float getPositiveFloatParameter(String name, float defaultValue) {
        float v = getFloatParameter(name, defaultValue);
        return v > 0 ? v : defaultValue;
    }

    /**
     * 获取double型参数. 参数不合法时返回{@code 0}
     *
     * @param name 参数名
     * @return 参数值或{@code 0}
     */
    public double getDoubleParameter(String name) {
        return getDoubleParameter(name, 0);
    }

    /**
     * 获取double型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public double getDoubleParameter(String name, double defaultValue) {
        return getParameter(name, Double.class, defaultValue);
    }

    /**
     * 获取正的double型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public double getPositiveDoubleParameter(String name, double defaultValue) {
        double v = getDoubleParameter(name, defaultValue);
        return v > 0 ? v : defaultValue;
    }

    /**
     * 获取String型参数. 参数不合法时返回{@code null}
     *
     * @param name 参数名
     * @return 参数值或{@code null}
     */
    public String getParameter(String name) {
        return getParameter(name, String.class);
    }

    /**
     * 获取String型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public String getParameter(String name, String defaultValue) {
        return getParameter(name, String.class, defaultValue);
    }

    /**
     * 获取Enum型参数. 参数不合法时返回{@code defaultValue}
     *
     * @param name 参数名
     * @param enumType 枚举类型
     * @param defaultValue 默认值
     * @return 参数值
     */
    public <T extends Enum<T>> T getEnumParameter(String name, Class<T> enumType, T defaultValue) {
        return getParameter(name, enumType, defaultValue);
    }

    /**
     * 获取Date型参数. 格式化模板：{@code yyyy-MM-dd}
     * <p>
     * 无法解析成日期时返回{@code defaultValue}</p>
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public Date getDateParameter(String name, Date defaultValue) {
        String value = requireParameter(name, String.class);
        try {
            return DateUtils.parseDate(value, "yyyy-MM-dd");
        } catch (ParseException ex) {
            return defaultValue;
        }
    }

    /**
     * 获取Date型参数. 格式化模板：{@code yyyy-MM-dd HH:mm:ss}
     * <p>
     * 无法解析成日期时返回{@code defaultValue}</p>
     *
     * @param name 参数名
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public Date getTimeParameter(String name, Date defaultValue) {
        String value = requireParameter(name, String.class);
        try {
            return DateUtils.parseDate(value, "yyyy-MM-dd HH:mm:ss");
        } catch (ParseException ex) {
            return defaultValue;
        }
    }

    /**
     * 使用指定pattern获取Date型参数.
     * <p>
     * 无法解析成日期时返回{@code defaultValue}</p>
     *
     * @param name 参数名
     * @param pattern 日期格式
     * @param defaultValue 默认值
     * @return 参数值或{@code defaultValue}
     */
    public Date getDateTimeParameter(String name, String pattern, Date defaultValue) {
        String value = requireParameter(name, String.class);
        try {
            return DateUtils.parseDate(value, pattern);
        } catch (ParseException ex) {
            return defaultValue;
        }
    }

}
